/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package projettni;

import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.PixelGrabber;
import java.io.*;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;

/**
 *
 * @author Didier
 */
public class ProjetTNIImage {

    public static final Integer TAILLE_SERIE = 3;
    /**
     * @param args
     */
    public static void main(String[] args) throws IOException {


        int[] dico = creerDictionnaireImage8Bit();
        Map<Integer, Float> probas = calculerProbasImage(dico);
        Map<Integer, Float> probasCumulees = calculerProbasCumuleesImage(probas);


        byte[][] tags = compresserImage();
        
        int taille = 0;
        for(int i=0; i<tags.length; i++){
            taille += tags[i].length;
        }
        System.out.println("Taille totale : " + taille);
        
        BufferedOutputStream bos = null;
        String fileName = "lena.txt";
        bos = new BufferedOutputStream(new FileOutputStream(fileName, true));
        for(int i=0; i<tags.length; i++){
           bos.write(tags[i]); 
        }

    }

    public static byte[][] compresserImage() throws IOException{

        int[] dico = creerDictionnaireImage8Bit();
        Map<Integer, Float> probas = calculerProbasImage(dico);
        Map<Integer, Float> probasCumulees = calculerProbasCumuleesImage(probas);

        String fichier = "lena_gray.gif";
        File f = new File(fichier);
        BufferedImage b = ImageIO.read(f);
        byte[] pixelsByte = getPixels(b);
        int[] tabPixels = Utilitaires.conversionPixelsImage(pixelsByte);

        //tableau de tags
        int nb_tags = (tabPixels.length / TAILLE_SERIE)+1;
        byte[][] tabTags = new byte[nb_tags][];

        //découpage du texte en série de TAILLE_CODAGE caractères
        for(int i=0; i<nb_tags; i++){
            int[] serie = new int[TAILLE_SERIE];
            int debut_cpt = i * TAILLE_SERIE;
            
            for(int j=0; j<TAILLE_SERIE; j++){
                if(debut_cpt+j < tabPixels.length){
                    serie[j] = tabPixels[debut_cpt+j];
                } else {
                    serie[j] = 0;
                }
            }
            tabTags[i] = calculerTagBinaireImage(serie, probas, probasCumulees, dico);
        }

        return tabTags;
    }

    /**
     * Retourne le tag de la série
     * @return
     */
    public static double calculerTagImage(int[] tabPixels, Map<Integer, Float> proba,
            Map<Integer, Float> probasCumulees, int[] dictionnaire) {

        double fLn = 0F;
        double fUn = 1F;
        double fPcum = 0F;
        double fPelem = 0F;
        double fTag = 0F;

        //parcours du tableau de valeurs
        for (int i = 0; i < tabPixels.length; i++) {

            fPcum = probasCumulees.get(tabPixels[i]);
            fPelem = proba.get(tabPixels[i]);

            double tempfLn = Utilitaires.calc_ln(fLn, fUn, fPcum);
            double tempfUn = Utilitaires.calc_un(fLn, fUn, fPcum, fPelem);
            fLn = tempfLn;
            fUn = tempfUn;
            System.out.println("ITERATION " + i + " - Ln :" + fLn + " Un:" + fUn);
        }

        fTag = (fUn + fLn) / 2F;
        return fTag;
    }
    
    /**
     * Retourne le tag de la série
     * @return
     */
    public static byte[] calculerTagBinaireImage(int[] tabPixels, Map<Integer, Float> proba,
            Map<Integer, Float> probasCumulees, int[] dictionnaire) {

        double fLn = 0F;
        double fUn = 1F;
        double fPcum = 0F;
        double fPelem = 0F;

        //parcours du tableau de valeurs
        for (int i = 0; i < tabPixels.length; i++) {

            fPcum = probasCumulees.get(tabPixels[i]);
            fPelem = proba.get(tabPixels[i]);

            double tempfLn = Utilitaires.calc_ln(fLn, fUn, fPcum);
            double tempfUn = Utilitaires.calc_un(fLn, fUn, fPcum, fPelem);
            fLn = tempfLn;
            fUn = tempfUn;
            System.out.println("ITERATION " + i + " - Ln :" + fLn + " Un:" + fUn);
        }

        return Utilitaires.tagToBinaire(fLn , fUn);
    }

    /**
     * Retourne un tableau d'entiers correspondants aux pixels contenus dans le tag
     * @return
     */
    public static int[] decoderTagImage(double tag, Map<Integer, Float> proba,
            Map<Integer, Float> probasCumulees, int[] dictionnaire) {

        double fLn = 0F;
        double fUn = 1F;
        int tailleDecodage = TAILLE_SERIE;
        int[] resultat = new int[tailleDecodage];

        for (int cpt = 0; cpt < tailleDecodage; cpt++) {
            for (int i = 0; i < probasCumulees.size(); i++) {
                double tempfLn = Utilitaires.calc_ln(fLn, fUn, probasCumulees.get(dictionnaire[i]));
                double tempfUn = Utilitaires.calc_un(fLn, fUn, probasCumulees.get(dictionnaire[i]),
                                proba.get(dictionnaire[i]));

                if(tempfLn < tag && tag < tempfUn){
                    resultat[cpt] = dictionnaire[i];
                    fLn = tempfLn;
                    fUn = tempfUn;
                    break;
                }
            }
        }
        return resultat;
    }

    /**
     * Retourne le dictionnaire de caractères pour une image 8 bits
     * @return
     */
    public static int[] creerDictionnaireImage8Bit() {
        int max = 256;
        int[] dictionnaire = new int[max];
        for (int i = 0; i < max; i++) {
            dictionnaire[i] = i;
        }
        return dictionnaire;
    }

    /**
     * Méthode retournant un tableau contenant la valeur des pixels d'une image
     * L'image est lu de gauche à droite et de haut en bas
     *
     * @param image
     * @return
     */
    public static byte[] getPixels(Image image) {

        byte[] pixels = null;
        PixelGrabber pg = null;
        if (image != null) {
            int w = image.getWidth(null);
            int h = image.getHeight(null);
            pixels = new byte[w * h];
            pg = new PixelGrabber(image, 0, 0, h, w, false);
            try {
                pg.grabPixels();
                pixels = (byte[]) pg.getPixels();
            } catch (Exception e) {

                System.out.println("Pixel Grabbing interrupted!");
                return null;
            }
        }
        return pixels;
    }

    /**
     * Retourne les probabilités d'occurence de chaque caratère du dictionnaire passé en paramètre
     * dans le fichier lena_gray.gif
     * @param dictionnaire
     * @return
     */
    public static Map<Integer, Float> calculerProbasImage(int[] dictionnaire) {
        //variables
        Map<Integer, Float> probabiliteDictionnaire = new HashMap<Integer, Float>();
        String fichier = "lena_gray.gif";
        File f = new File(fichier);
        BufferedImage b;

        try {
            b = ImageIO.read(f);
            byte[] pixelsByte = getPixels(b);

            //initialisation de la map de probas à 0
            for (int i=0; i<dictionnaire.length; i++) {
                probabiliteDictionnaire.put(dictionnaire[i], 0F);
            }
            
            //conversion des bytes en int
            int[] pixels = Utilitaires.conversionPixelsImage(pixelsByte);

            //calcul du nombre d'occurences
            for (int p : pixels) {
                //si le caractère existe
                if (probabiliteDictionnaire.containsKey(p)) {
                    Float valPixel = probabiliteDictionnaire.get(p);
                    probabiliteDictionnaire.put(p, valPixel + 1F);
                }
            }

            //calcul des probas
            for (Integer mapKey : probabiliteDictionnaire.keySet()) {
                Float proba = probabiliteDictionnaire.get(mapKey) / Float.valueOf(pixels.length);
                probabiliteDictionnaire.put(mapKey, proba);
            }

        } catch (IOException ex) {
            Logger.getLogger(ProjetTNIImage.class.getName()).log(Level.SEVERE, null, ex);
        }
        return probabiliteDictionnaire;
    }

    /**
     * Retourne les probabilités d'occurence cumulées de chaque caratère du dictionnaire passé en paramètre
     * dans le fichier lena_gray.gif
     * @param dictionnaire
     * @return
     */
    public static Map<Integer, Float> calculerProbasCumuleesImage(Map<Integer, Float> probas) {
        //variables
        Map<Integer, Float> probabiliteCumulees = new HashMap<Integer, Float>();
        Float sommeProbas = 0F;

        probabiliteCumulees.put(0, 0F);
        for (int i = 1; i < probas.size(); i++) {
            sommeProbas += probas.get(i - 1);
            probabiliteCumulees.put(i, sommeProbas);
        }

        return probabiliteCumulees;
    }

    /**
     * ANCIEN ALGO DE CALCUL DE L'ENTROPIE
     */
    public static void algoLectureCreationDictionnaire() {
        /**
        //lecture du fichier texte
        try{

        //variables
        String fichier ="exemple1.txt";
        BufferedReader br = new BufferedReader(
        new InputStreamReader(
        new FileInputStream(fichier)));
        String ligne;
        int nb_char = 0;
        Map<String,Integer> dictionnaire = new HashMap<String, Integer>();

        //parcours du fichier
        while ((ligne = br.readLine()) != null){
        ligne.toLowerCase();
        System.out.println(ligne);
        nb_char += ligne.length();

        for (int i = 0 ; i < ligne.length() ;i++){

        String caractere = String.valueOf(ligne.charAt(i));

        //ajout du caractre dans le dictionnaire s'il n'existe pas
        if(!dictionnaire.containsKey(caractere)){
        dictionnaire.put(caractere, 1);
        } else { //incrmentation
        Integer cpt_car = dictionnaire.get(caractere);
        cpt_car++;
        dictionnaire.put(caractere, cpt_car);
        }
        }
        }

        Integer somme = 0;
        //affichage des occurences de caractres
        for(String mapKey : dictionnaire.keySet()){
        System.out.println(mapKey+" : "+dictionnaire.get(mapKey));
        somme += dictionnaire.get(mapKey);
        }
        System.out.println("Somme" +somme);

        //construction des probas
        Map<String,Float> probabiliteDictionnaire = new HashMap<String, Float>();
        for(String mapKey : dictionnaire.keySet()){
        Float proba = dictionnaire.get(mapKey)/Float.valueOf(nb_char);
        probabiliteDictionnaire.put(mapKey, proba);
        }

        //affichage des probas
        Float sommeProba = 0F;
        for(String mapKey : probabiliteDictionnaire.keySet()){
        System.out.println(mapKey+" : "+probabiliteDictionnaire.get(mapKey));
        sommeProba += probabiliteDictionnaire.get(mapKey);
        }

        System.out.println("Somme proba : "+ sommeProba);

        //calcul de l'entropie
        double entropie = 0;
        for(String mapKey : probabiliteDictionnaire.keySet()){
        Float proba = probabiliteDictionnaire.get(mapKey);
        entropie += proba * (Math.log10(proba)/Math.log10(2));
        }
        System.out.println("Entropie :"+ -entropie);

        System.out.println("Nb caractres diffrents : "+dictionnaire.size());

        System.out.println("Nombre de caractres : "+nb_char);

        br.close();
        }
        catch (Exception e){
        System.out.println(e.toString());
        }*/
    }
}
